//
//  PageMenuContrainer.swift
//  Pods
//
//  Created by HOA on 2020/12/29.
//

import UIKit
// http://stackoverflow.com/questions/1560081/how-can-i-create-a-uicolor-from-a-hex-string
public extension UIColor {
    class func hexColor(_ hex: UInt32) -> UIColor {
        let r = CGFloat((hex & 0xff0000) >> 16) / 255.0
        let g = CGFloat((hex &   0xff00) >>  8) / 255.0
        let b = CGFloat((hex &     0xff)) / 255.0
        return UIColor(red:r, green:g, blue: b, alpha:1.0)
    }
}

//  UIFont.systemFont(ofSize: fontSize, weight: UIFontWeightMedium)
// 计算文字高度或者宽度与weight参数无关
extension String {
    func width(fontSize: CGFloat, height: CGFloat = 15) -> CGFloat {
        let font = UIFont.systemFont(ofSize: fontSize)
        let rect = NSString(string: self).boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: height), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
        return ceil(rect.width)
    }
}

let kRightItemWidth: CGFloat = 30.0
let spaceInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)

let kMenuItemHeight: CGFloat = 30.0

let kMenuItemBaseTag: Int = 170602

let kSeparatorHeight: CGFloat =  6.0
let kIndicatorHeight: CGFloat =  kSeparatorHeight

let kUnderscoreWidth: CGFloat = 20.0
let kUnderscoreHeight: CGFloat = 4.0
let kUnderscoreCornerRadius: CGFloat = 2
let kUnderscoreColor: UIColor = UIColor.hexColor(0xCA2626)

let kScrolleBottomPadding: CGFloat = 10

public class PageMenuContainer: UIViewController {
    
    public weak var delegate: PageMenuContainerDelegate? = nil
    
    public internal(set) var menuStyle: PageMenuContainerStyle = .plain
    public internal(set) var menuStyleDesign: PageMenuItemDesign = PageMenuItemDesign()
    public internal(set) var titles: [String] = []
    public internal(set) var childControllers: [UIViewController] = []
    
    private var iconStyle: PageMenuContainerStyle! = nil
    private var iconStyleDesign: PageMenuItemDesign! = nil
    private var iconIndex: Int = 0
    
    private var isUsedIconStyle: Bool {
        get {
            iconStyle != nil && iconStyleDesign != nil
        }
    }
    
    private var startIndex: Int = 0
    
    private var topBarHeight: CGFloat = 40.0
    private var itemMargin: CGFloat = 0.0
    private var separatorHeight: CGFloat = kSeparatorHeight
    private var indicatorHeight: CGFloat = kIndicatorHeight
    
    private var underscore: UIView = UIView()
    public var menuItems: [PageMenuItem] = []
    
    private var rightItem: UIView!
    
    private lazy var pageViewController: UIPageViewController = {
        let style: UIPageViewController.TransitionStyle = .scroll
        let pageViewController = UIPageViewController(transitionStyle: style, navigationOrientation: .horizontal, options: nil)
        pageViewController.delegate = self
        pageViewController.dataSource = self
        return pageViewController
    }()
    
    private lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.backgroundColor = .clear
        scrollView.delegate = self
        scrollView.bounces = false
        scrollView.scrollsToTop = false
        scrollView.isPagingEnabled = false
        scrollView.showsHorizontalScrollIndicator = false
        return scrollView
    }()
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        
        changeMenuItem(at: startIndex)
        
        addChild(pageViewController)
        self.view.addSubview(pageViewController.view)
        
        let  width: CGFloat = self.view.bounds.size.width
        let height: CGFloat = containerHeight ?? self.view.bounds.size.height
        
        let x: CGFloat = 0.0
        let y: CGFloat = topBarHeight + scrollView.frame.size.height
        let w: CGFloat = width
        let h: CGFloat = height - y
        pageViewController.view.frame = CGRect(x: x, y: y, width: w, height: h)
        pageViewController.didMove(toParent: self)
        
        delegate?.pageMenuContrainer(self, didPrepare: menuItems)
    }
    
    
    // Designated Initializer
    public required init(coder aDecoder: NSCoder) {
        fatalError("NSCoder not supported")
    }
    private var containerHeight: CGFloat!
    public init(controllers: [UIViewController],
                menuStyle: PageMenuContainerStyle = .plain,
                menuStyleDesign: PageMenuItemDesign = PageMenuItemDesign(),
                startIndex: Int = 0,
                topBarHeight: CGFloat,
                iconStyle: PageMenuContainerStyle! = nil,
                iconStyleDesign: PageMenuItemDesign! = nil,
                iconIndex: Int = 0,
                rightButton: UIButton! = nil,
                containerHeight: CGFloat! = nil) {

        super.init(nibName: nil, bundle: nil)
        self.containerHeight = containerHeight
        self.menuStyleDesign = menuStyleDesign
        self.menuStyle = menuStyle
        self.startIndex = startIndex > 0 && startIndex < controllers.count
            ? startIndex - 1
            : 0
        self.topBarHeight = topBarHeight
        self.currentIndex = 0
        
        self.iconStyle = iconStyle
        self.iconStyleDesign = iconStyleDesign
        self.iconIndex = iconIndex
        
        self.childControllers = controllers
        
        self.rightItem = rightButton
        
        self.titles = controllers.enumerated().map {
            // $0.0 ... index
            // $0.1 ... UIViewController
            if let title = $0.1.value(forKey: "title") as? String, title.count > 0 {
                return title
            }
            else {
                return String(format: "Title%zd", $0.0 + 1)
            }
        }
        
        prepareForMenuStyle(menuStyle)
    }
    
    public override func loadView() {
        super.loadView()
        
        let width: CGFloat = self.view.bounds.size.width
        
        let x: CGFloat = spaceInset.left
        let y: CGFloat = topBarHeight
        let w: CGFloat = rightItem == nil ? (width - x) : width - kRightItemWidth - spaceInset.right - x
        let h: CGFloat = kMenuItemHeight + separatorHeight + kUnderscoreHeight + kScrolleBottomPadding
        
        scrollView.frame = CGRect(x: x, y: y, width: w, height: h)
        self.view.addSubview(scrollView)
        
        prepareForMenuItems()
        prepareForMenuSeparator()
        prepareForMenuIndicator()
        prepareForRightItem()
    }
    
    // MARK: - Properties
    var currentIndex: Int = 0 {
        willSet {
            let index = currentIndex
            let viewController: UIViewController = self.childControllers[index]
            delegate?.pageMenuContrainer(self, didMoveTo: viewController, at: index)
            
            let item: PageMenuItem = self.menuItems[index]
            item.isSelected = false
        }
        didSet {
            self.moveIndicator(at: currentIndex)
        }
    }
    
    // MARK: - Private Methods for Indicator
    func moveIndicator(at index: Int) {
        
        self.willMoveIndicator(at: index)
        
        let item: PageMenuItem = self.menuItems[index]
        switch (menuStyle) {
        case .plain:
            UIView.animate(withDuration: 0.2) {
                self.underscore.center.x = item.center.x
            }
        default:
            break
        }
        item.isSelected = true
        
        delegate?.pageMenuContrainer(self, didSelect: item, at: index)
    }
    
    /// 点击的item如果不在显示区域内
    func willMoveIndicator(at index: Int) {
        let w: CGFloat =  menuItems[index].bounds.width + itemMargin
        var x: CGFloat = menuItems[index].frame.maxX
        let y: CGFloat = 0.0
        
        let  width: CGFloat = scrollView.frame.size.width
        // 選択したタブを中央寄せにする計算
        let   size:  CGSize = scrollView.contentSize
        let  leftX: CGFloat = (width - w) * 0.5 // 画面幅の半分からタブ幅の半分を引く
        let   tabN: CGFloat = ceil(width / w) // 画面内に見えるタブの数
        let rightX: CGFloat = size.width - floor((tabN * 0.5 + 0.5) * w)
        if (x <  leftX) { x  = 0.0 }
        else if (x > rightX) { x  = size.width - width }
        else         { x -= leftX }
        
        if menuItems.count > 3 {
            scrollView.setContentOffset(CGPoint(x: x, y: y), animated: true)
        }
    }
}

// MARK: - Prepartions
extension PageMenuContainer {
    // CUSTOM: Add the settings for your custom menu style here.
    func prepareForMenuStyle(_ menuStyle: PageMenuContainerStyle) {
        switch (menuStyle) {
        case .plain:
            itemMargin = 6
            separatorHeight = 1.0
            indicatorHeight = 3.0
        case .cornerRadiusButton, .iconButton:
            itemMargin = 6
            separatorHeight = 0.0
            indicatorHeight = 0.0
        }
    }
    
    // CUSTOM: Add the separator color for your custom menu style if needed.
    func prepareForMenuSeparator() {
        
    }
    
    // CUSTOM: Add the indicator color for your custom menu style if needed.
    func prepareForMenuIndicator() {
        
        let x: CGFloat = 0.0
        let y: CGFloat = scrollView.frame.size.height - kUnderscoreHeight - kScrolleBottomPadding
        
        underscore.frame = CGRect(x: x, y: y, width: kUnderscoreWidth, height: kUnderscoreHeight)
        underscore.layer.cornerRadius = kUnderscoreCornerRadius
        underscore.layer.masksToBounds = true
        underscore.backgroundColor = menuStyle == PageMenuContainerStyle.plain ? kUnderscoreColor : .clear
        scrollView.addSubview(underscore)
    }
    
    func prepareForRightItem() {
        if let rightItem = rightItem {
            let itemSize = CGSize(width: kRightItemWidth, height: kRightItemWidth)
            let x = UIScreen.main.bounds.width - kRightItemWidth - spaceInset.right
            let y = topBarHeight
            rightItem.frame = CGRect(origin: CGPoint(x: x, y: y), size: itemSize)
            view.addSubview(rightItem)
        }
    }
    
    fileprivate func changeMenuItem(at index: Int) {
        let  viewController: UIViewController = self.childControllers[index]
        let viewControllers: [UIViewController] = [viewController]
        let direction: UIPageViewController.NavigationDirection = (index > currentIndex) ? .forward : .reverse
        self.currentIndex = index
        pageViewController.setViewControllers(viewControllers, direction: direction, animated: true, completion: nil)
    }
    
    @objc func handleSingleTap(_ gesture: UITapGestureRecognizer) {
        if var index: Int = gesture.view?.tag {
            index -= kMenuItemBaseTag
            changeMenuItem(at: index)
        }
    }
    
    /*
     * swift3 - how to create instance of a class from a string in swift 3
     * https://stackoverflow.com/questions/40373030/how-to-create-instance-of-a-class-from-a-string-in-swift-3
     */
    func stringClassFromString(_ className: String) -> AnyClass! {
        // get namespace
        let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
        // XXX: 順に "namespace" を変更して探索を試みる
        if let cls: AnyClass = NSClassFromString("PageMenuHiKit.\(className)") {
            return cls
        }
        if let cls: AnyClass = NSClassFromString("PageMenuContainer.\(className)") {
            return cls
        }
        // get 'anyClass' with classname and namespace
        let cls: AnyClass = NSClassFromString("\(namespace).\(className)")!
        // return AnyClass
        return cls
    }
    
    // CUSTOM: Add the code to create instance of your custom menu style.
    func prepareForMenuItems() {
        self.menuItems = []
        
        
        var x: CGFloat = itemMargin
        let y: CGFloat = 0.0
        let h: CGFloat = kMenuItemHeight - y
        
        let count: Int = self.titles.count
        for i in 0 ..< count {
            let title: String = self.titles[i]
            var w: CGFloat = 0
            var design = menuStyleDesign
            var className = menuStyle.className()

            if isUsedIconStyle && iconIndex == i {
                w = title.width(fontSize: iconStyleDesign.selectedFont.pointSize) + 20
                design = iconStyleDesign
                className = iconStyle.className()
            }
            else {
                w = title.width(fontSize: menuStyleDesign.selectedFont.pointSize) + 6
            }
            
            if menuStyle == .cornerRadiusButton {
                w += 10
            }
            
            let frame: CGRect = CGRect(x: x, y: y, width: w, height: h)
            
            let classType = stringClassFromString(className) as! PageMenuItem.Type
            let item: PageMenuItem = classType.init(frame: frame, title: title, design: design)
            item.tag = kMenuItemBaseTag + i
            scrollView.addSubview(item)
            x += (w + itemMargin)
            
            item.isSelected = (i == 0)
            self.menuItems.append(item)
            
            let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
            item.addGestureRecognizer(tapGesture)
        }
        
        let  width: CGFloat = scrollView.bounds.size.width
        let height: CGFloat = scrollView.bounds.size.height
        scrollView.contentSize = CGSize(width: x, height: height)
        
        var frame: CGRect = scrollView.frame
//        if (width > x) { // 居中
//            frame.origin.x = floor((width - x) * 0.5)
//            frame.size.width = x
//        }
//        else {
        frame.origin.x = spaceInset.left
        frame.size.width = rightItem == nil ? width : width - kRightItemWidth - spaceInset.right
//        }
        scrollView.frame = frame
    }
    
}
// MARK: - Public Methods
extension PageMenuContainer {
    
    public func setMenuSeparatorColor(_ color: UIColor) {
        
    }
    
    public func setMenuIndicatorColor(_ color: UIColor) {
        
    }
    
}
// MARK: UIScrollViewDelegate
extension PageMenuContainer: UIScrollViewDelegate {
    
    
    
}
// MARK:  UIPageViewControllerDelegate, UIPageViewControllerDataSource
extension PageMenuContainer: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
    
    // MARK - UIPageViewControllerDelegate (optional)
    public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
        if let viewController: UIViewController = pendingViewControllers.last {
            if let index = self.childControllers.firstIndex(of: viewController) {
                if index != currentIndex {
                    self.willMoveIndicator(at: index)
                }
                delegate?.pageMenuContrainer(self, willMoveTo: viewController, at: index)
            }
        }
    }
    
    // MARK - UIPageViewControllerDelegate (optional)
    public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if let viewController: UIViewController = pageViewController.viewControllers?.last {
            guard let index = self.childControllers.firstIndex(of: viewController) else { return }
            if completed {
                self.currentIndex = index
            }
            else {
                self.willMoveIndicator(at: index)
            }
        }
    }
    
    // MARK - UIPageViewControllerDelegate (optional)
    public func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation) -> UIPageViewController.SpineLocation {
        if orientation.isPortrait || UIDevice.current.userInterfaceIdiom == .phone {
            if let currentViewController: UIViewController = pageViewController.viewControllers?.first {
                let viewControllers: [UIViewController] = [currentViewController]
                pageViewController.setViewControllers(viewControllers, direction: .forward, animated: true, completion: nil)
            }
            pageViewController.isDoubleSided = false
            return .min
        }
        return .none
    }
    
    
    // MARK - UIPageViewControllerDataSource (required)
    public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        if let index = self.childControllers.firstIndex(of: viewController) {
            if index != 0 && index != NSNotFound {
                return self.childControllers[index - 1]
            }
        }
        return nil
    }
    
    // MARK - UIPageViewControllerDataSource (required)
    public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        if let index = self.childControllers.firstIndex(of: viewController) {
            let count: Int = self.childControllers.count
            if index != NSNotFound && index + 1 < count {
                return self.childControllers[index + 1]
            }
        }
        return nil
    }
    
}
